﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace RuEn
{
    /// <summary>
    /// Attribute class for Comparer Rules. Compare one value with another using the defined Criteria
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
    public class ComparerObjectRule : CompareRuleBase
    {
        private string _errorMessage = "String Comparions failed !!!";
        private object _comparer = null;
        private CompareObjectEnum _operation;

        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="comparer">Value to compare with</param>
        /// <param name="operation">Comparision Criteria</param>
        public ComparerObjectRule(object comparer, CompareObjectEnum operation)
        {
            _comparer = comparer;
            _operation = operation;
        }

        /// <summary>
        /// Overloaded Constructor
        /// </summary>
        /// <param name="comparer">Value to compare with</param>
        /// <param name="ErrorMessage">Custom Error Message</param>
        /// <param name="operation">Comparision Criteria</param>
        public ComparerObjectRule(object comparer, string ErrorMessage, CompareObjectEnum operation)
        {
            _comparer = comparer;
            _operation = operation;
            _errorMessage = ErrorMessage;
        }

        /// <summary>
        /// Public property for Custom Error Message
        /// </summary>
        public string ErrorMessage
        {
            get { return _errorMessage; }
            set { _errorMessage = value; }
        }

        /// <summary>
        /// Public Property for value to compare with
        /// </summary>
        public object Comparer
        {
            get { return _comparer; }
            set { _comparer = value; }
        }

        /// <summary>
        /// Public Property for Compare criteria
        /// </summary>
        public CompareObjectEnum Operation
        {
            get { return _operation; }
            set { _operation = value; }
        }

        /// <summary>
        /// Validate the Object based on the criteria
        /// </summary>
        /// <param name="value">1st Object to Compare</param>
        /// <param name="Comparer">2nd Object to Compare With</param>
        /// <param name="ErrorMessage">Error Message</param>
        /// <param name="operation">Check Type</param>
        /// <returns></returns>
        public bool Validate(object value, object Comparer, string ErrorMessage, CompareObjectEnum operation)
        {
            if (value != null && Comparer != string.Empty)
            {
                switch (operation)
                {
                    case CompareObjectEnum.ReferenceEquel:
                        if (!value.Equals(Comparer))
                        {
                            BrokenRules.Add(ErrorMessage);
                            return false;
                        }
                        break;
                    case CompareObjectEnum.ReferenceNotEquel:
                        if (value.Equals(Comparer))
                        {
                            BrokenRules.Add(ErrorMessage);
                            return false;
                        }
                        break;
                    case CompareObjectEnum.ValueEquels:
                        if (!CheckObjectEquality(value, Comparer))
                        {
                            BrokenRules.Add(ErrorMessage);
                            return false;
                        }
                        break;
                    default:
                        if (!value.Equals(Comparer))
                        {
                            BrokenRules.Add(ErrorMessage);
                            return false;
                        }
                        break;
                }
            }
            return true;
        }

        private bool CheckObjectEquality(object value, object Comparer)
        {
            Type sourceType = value.GetType();
            Type destinationType = Comparer.GetType();
            if (sourceType == destinationType)    
            {        
                PropertyInfo[] sourceProperties = sourceType.GetProperties();
                foreach (PropertyInfo pi in sourceProperties)
                {
                    if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null &&
                        destinationType.GetProperty(pi.Name).GetValue(Comparer, null) == null))
                    {
                        // if both are null, don't try to compare  (throws exception)
                    }
                    else
                    {
                        if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(Comparer, null).ToString()))
                        {
                            // only need one property to be different to fail Equals.
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }


}
